﻿using System;
using System.Runtime.InteropServices;

namespace LFSRecord2.Model.Sound
{
    public enum WaveFormats
    {
        None = 0,
        Pcm = 1,
        Float = 3
    }

    [StructLayout(LayoutKind.Sequential)]
    public class WaveFormatEx
    {
        public ushort wFormatTag;
        public ushort nChannels;
        public uint nSamplesPerSec;
        public uint nAvgBytesPerSec;
        public ushort nBlockAlign;
        public ushort wBitsPerSample;
        public ushort cbSize;

        public WaveFormatEx(uint rate, int bits, int channels)
        {
            wFormatTag = (ushort)WaveFormats.Pcm;
            nChannels = (ushort)channels;
            nSamplesPerSec = rate;
            wBitsPerSample = (ushort)bits;
            cbSize = 0;

            nBlockAlign = (ushort)(channels * (bits / 8.0f));
            nAvgBytesPerSec = nSamplesPerSec * nBlockAlign;
        }
    }

    [Flags]
    public enum WaveDeviceFormats
    {
        None = 0x00000000,
        WAVE_FORMAT_1M08 = 0x00000001,
        WAVE_FORMAT_1M16 = 0x00000002,
        WAVE_FORMAT_1S08 = 0x00000004,
        WAVE_FORMAT_1S16 = 0x00000008,
        WAVE_FORMAT_2M08 = 0x00000010,
        WAVE_FORMAT_2M16 = 0x00000020,
        WAVE_FORMAT_2S08 = 0x00000040,
        WAVE_FORMAT_2S16 = 0x00000080,
        WAVE_FORMAT_4M08 = 0x00000100,
        WAVE_FORMAT_4M16 = 0x00000200,
        WAVE_FORMAT_4S08 = 0x00000400,
        WAVE_FORMAT_4S16 = 0x00000800,
        WAVE_FORMAT_48M08 = 0x00001000,
        WAVE_FORMAT_48S08 = 0x00002000,
        WAVE_FORMAT_48M16 = 0x00004000,
        WAVE_FORMAT_48S16 = 0x00008000,
        WAVE_FORMAT_96M08 = 0x00010000,
        WAVE_FORMAT_96M16 = 0x00020000,
        WAVE_FORMAT_96S08 = 0x00040000,
        WAVE_FORMAT_96S16 = 0x00080000,
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct WaveOutCaps
    {
        public ushort wMid;
        public ushort wPid;
        public uint vDriverVersion;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string szPname;
        public uint dwFormats;
        public ushort wChannels;
        public ushort wReserved1;
        public uint dwSupport;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct WaveInCaps
    {
        public ushort wMid;
        public ushort wPid;
        public uint vDriverVersion;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string szPname;
        public uint dwFormats;
        public ushort wChannels;
        public ushort wReserved1;
    }

    internal class NativeMethods
    {
        // Consts
        public const int MMSYSERR_NOERROR = 0; // no error

        public const int MM_WOM_OPEN = 0x3BB;
        public const int MM_WOM_CLOSE = 0x3BC;
        public const int MM_WOM_DONE = 0x3BD;

        public const int MM_WIM_OPEN = 0x3BE;
        public const int MM_WIM_CLOSE = 0x3BF;
        public const int MM_WIM_DATA = 0x3C0;

        public const int CALLBACK_FUNCTION = 0x00030000;    // dwCallback is a FARPROC 

        public const int TIME_MS = 0x0001;  // time in milliseconds 
        public const int TIME_SAMPLES = 0x0002;  // number of wave samples 
        public const int TIME_BYTES = 0x0004;  // current byte offset 

        // Callbacks
        public delegate void WaveDelegate(IntPtr hdrvr, int uMsg, int dwUser, ref WaveHdr wavhdr, int dwParam2);

        // Structs
        [StructLayout(LayoutKind.Sequential)]
        public struct WaveHdr
        {
            public IntPtr lpData; // pointer to locked data buffer
            public int dwBufferLength; // length of data buffer
            public uint dwBytesRecorded; // used for input only
            public IntPtr dwUser; // for client's use
            public uint dwFlags; // assorted flags (see defines)
            public uint dwLoops; // loop control counter
            public IntPtr lpNext; // PWaveHdr, reserved for driver
            public IntPtr reserved; // reserved for driver
        }

        // WaveOut calls
        [DllImport("winmm.dll")]
        public static extern int waveOutGetNumDevs();
        [DllImport("winmm.dll")]
        public static extern int waveOutGetDevCaps(int uDeviceID, ref WaveOutCaps pwoc, int cbwoc);
        [DllImport("winmm.dll")]
        public static extern int waveOutPrepareHeader(IntPtr hWaveOut, ref WaveHdr lpWaveOutHdr, int uSize);
        [DllImport("winmm.dll")]
        public static extern int waveOutUnprepareHeader(IntPtr hWaveOut, ref WaveHdr lpWaveOutHdr, int uSize);
        [DllImport("winmm.dll")]
        public static extern int waveOutWrite(IntPtr hWaveOut, ref WaveHdr lpWaveOutHdr, int uSize);
        [DllImport("winmm.dll")]
        public static extern int waveOutOpen(out IntPtr hWaveOut, int uDeviceID, WaveFormatEx lpFormat, WaveDelegate dwCallback, IntPtr dwInstance, int dwFlags);
        [DllImport("winmm.dll")]
        public static extern int waveOutReset(IntPtr hWaveOut);
        [DllImport("winmm.dll")]
        public static extern int waveOutClose(IntPtr hWaveOut);
        [DllImport("winmm.dll")]
        public static extern int waveOutPause(IntPtr hWaveOut);
        [DllImport("winmm.dll")]
        public static extern int waveOutRestart(IntPtr hWaveOut);
        [DllImport("winmm.dll")]
        public static extern int waveOutGetPosition(IntPtr hWaveOut, out int lpInfo, int uSize);
        [DllImport("winmm.dll")]
        public static extern int waveOutSetVolume(IntPtr hWaveOut, int dwVolume);
        [DllImport("winmm.dll")]
        public static extern int waveOutGetVolume(IntPtr hWaveOut, out int dwVolume);

        // WaveIn calls
        [DllImport("winmm.dll")]
        public static extern int waveInGetNumDevs();
        [DllImport("winmm.dll")]
        public static extern int waveInGetDevCaps(int uDeviceID, ref WaveInCaps pwic, int cbwic);
        [DllImport("winmm.dll")]
        public static extern int waveInAddBuffer(IntPtr hWaveIn, ref WaveHdr pwh, int cbwh);
        [DllImport("winmm.dll")]
        public static extern int waveInClose(IntPtr hWaveIn);
        [DllImport("winmm.dll")]
        public static extern int waveInOpen(out IntPtr phwi, int uDeviceID, WaveFormatEx lpFormat, WaveDelegate dwCallback, IntPtr dwInstance, int dwFlags);
        [DllImport("winmm.dll")]
        public static extern int waveInPrepareHeader(IntPtr hWaveIn, ref WaveHdr lpWaveInHdr, int uSize);
        [DllImport("winmm.dll")]
        public static extern int waveInUnprepareHeader(IntPtr hWaveIn, ref WaveHdr lpWaveInHdr, int uSize);
        [DllImport("winmm.dll")]
        public static extern int waveInReset(IntPtr hWaveIn);
        [DllImport("winmm.dll")]
        public static extern int waveInStart(IntPtr hWaveIn);
        [DllImport("winmm.dll")]
        public static extern int waveInStop(IntPtr hWaveIn);
    }
}
